/******************************************************
 * Copyright (c) 2012, LIJUN All rights reserved *
 ******************************************************/

/**
 * TouchSlide接口
 * @author carlli
 * @encode utf-8
 * @version 1.0
 * @data 2012.9.24
 * @modify
 * -------------------------------------------------------------------
 * 
 * -------------------------------------------------------------------
 * @usage
 * -------------------------------------------------------------------
 *
 * -------------------------------------------------------------------
 */


Smart.define("touchslide", function(S){
    var G_TOUCH_MAP = null;       // TouchSlide实例MAP
    var G_CURRENT_TOUCH_SLIDE = null; //当前TouchSlide实例
    var G_SUPPORT_TOUCH = false; //是否支持touch
    var G_START_EVENT = ((G_SUPPORT_TOUCH = ("ontouchstart" in window)) ? "touchstart" : "mousedown"); //start事件
    var G_END_EVENT = (("ontouchend" in window) ? "touchend" : "mouseup");  //end事件
    var G_MOVE_EVENT = (("ontouchmove" in window) ? "touchmove" : "mousemove");  //move事件
    
    /**
     * 获取TouchSlide实例
     * @param String name 实例名称
     * @param Boolean create 在不存在时是否创建新的一个新的实例
     * @return TouchSlide slide
     */
    function GetTouchSlide(name, create){
        var instance = null;
        G_TOUCH_MAP = G_TOUCH_MAP || {};
        if(true === create){
            if(!G_TOUCH_MAP[name]){
                instance = G_TOUCH_MAP[name] = new _TouchSlide(name);
            }else{
                instance = G_TOUCH_MAP[name];
            }
        }else{
            instance = (G_TOUCH_MAP[name] || null);
        }
        return instance;
    };
    
    /**
     * 触摸幻灯片（构造函数）
     * @param String name
     */
    var _TouchSlide = function(name){
        this.name = name;       //名称，唯一标识
        this.fingerStartX = 0;  //手指X轴开始位置
        this.fingerStartY = 0;  //手指Y轴开始位置
        this.currentPoint = 0;  //当前位置点
        this.currentOffset = 0; //当前偏移值
        this.right = true;      //右滑动
        this.left = false;      //左滑动
        this.up = false;        //上滑动
        this.down = false;      //下滑动
        this.changed = false;   //是否有改变
        //--------------------------------------------
        this.attributes = {
            "touch" : null,     //缓存name属性
            "selector" : null,  //缓存CSS选择器
            "dir" : null,       //缓存移动方向 0：左右 1:上下
            "style" : null,     //缓存样式属性
            "size" : null,      //缓存每屏尺寸
            "touchsize" : null, //缓存滑动尺寸
            "page" : null,      //缓存总屏数
            "shift" : 0,        //缓存偏移值[0...1]
            "prevent" : "1",    //缓存阻止默认行为
            "bubble" : "0",     //缓存阻止冒泡
            "real" : null,      //缓存真实移动节点
            "start" : -1,       //起点位置
            "end" : -1          //结束位置
        };
        //--------------------------------------------
        this.on = {
            onstart : null,      //开始时的回调，{Function callback, Array args, Object context}
            onmove : null,       //移动时的回调，{Function callback, Array args, Object context}
            onend : null,        //结束时的回调，{Function callback, Array args, Object context}
            onbefore : null,     //开始前的回调{Function callback, Array args, Object context}
            onbeforecheck : null //开始前校验的回调{Function callback, Array args, Object context}
        };
    };
    //方法
    _TouchSlide.prototype = {
        /**
         * 检测是否支持此属性
         * @param String name 属性名
         * @return Boolean true/false
         */
        hasAttribute : function(name){
            return (name in this.attributes);
        },
        /**
         * 设置属性
         * @param String name 属性名
         * @param * value 属性值
         */
        setAttribute : function(name, value){
            if(this.hasAttribute(name)){
                this.attributes[name] = value;
            }
        },
        /**
         * 获取属性值
         * @param String name 属性名
         * @return * value 属性值
         */
        getAttribute : function(name){
            if(this.hasAttribute(name)){
                return this.attributes[name];
            }
            return null;
        },
        /**
         * 设置回调
         * @param String type 类型
         * @param Function callback 回调
         * @param Array args 参数
         * @param Object context 上下文
         */
        set : function(type, callback, args, context){
            var key = "on" + type;
            var rvs = "|beforecheck|";

            if(key in this.on){
                if(callback && (callback instanceof Function) && callback.apply){
                    this.on[key] = {
                        "callback" : callback,
                        "args" : args || [],
                        "context" : context || this,
                        "returnValue" : (rvs.indexOf("|"+type+"|") != -1)
                    };
                }else{
                    this.on[key] = null;
                }
            }
        },
        /**
         * 移除回调
         * @param String type 类型
         */
        remove : function(type){
            this.on["on" + type] = null;
        },
        /**
         * 获取回调
         * @param String type 类型
         * @return Object on
         */
        get : function(type){
            var key = "on" + type;
            var o = this.on;

            if(key in o){
                return o[key];
            }
            return null;
        },
        /**
         * 清除所有回调
         */
        clear : function(){
            for(var key in this.on){
                if(this.on.hasOwnProperty(key)){
                    this.remove(key.substr(2));
                }
            }
        },
        /**
         * 执行回调函数
         * @param String type 类型
         * @param Array args 消息
         * @return * result 返回值
         */
        execCallback : function(type, args){
            var o = this.get(type);
            var m = args || [];
            var a = [].concat(m);
            var result = undefined;

            if(o && o.callback){
                a = a.concat(o.args||[]);
                if(true === o.returnValue){
                    result = o.callback.apply((o.context||this), a);
                }else{
                    o.callback.apply((o.context||this), a);
                }
            }
            m = null; a = null;

            return result;
        },
        /**
         * touchstart事件监听回调函数
         * @param Event e
         */
        bindStartHandler : function(e){
            var slide = GetTouchSlide(this.getAttribute("data-touch"));
            G_CURRENT_TOUCH_SLIDE = slide;

            if("1" == slide.getAttribute("prevent")){
                S.Event.preventDefault();
            }
            if("0" == slide.getAttribute("bubble")){
                S.Event.stopPropagation();
            }

            var property = slide.getAttribute("style");
            var touch = G_SUPPORT_TOUCH ? e.targetTouches[0] : e;
            var target = G_SUPPORT_TOUCH ? this : document;

            slide.fingerStartX = touch.pageX || touch.clientX;
            slide.fingerStartY = touch.pageY || touch.clientY;

            //当前层的位置
            var start = slide.getAttribute("start");
            var isSetStart = (start > -1);
            slide.currentPoint = parseInt(this.style[property] || (isSetStart ? start : 0), 10);
            slide.changed = false;
            slide.currentOffset = 0;

            //touch move
            S.Event.on(target, G_MOVE_EVENT, slide.bindMoveHandler, false);
            S.Event.on(target, G_END_EVENT, slide.bindEndHandler, false);

            slide.execCallback("start", [e, slide]);
        },
        /**
         * touchmove事件监听回调函数
         * @param Event e
         */
        bindMoveHandler : function(e){
            var slide = G_CURRENT_TOUCH_SLIDE;
            if("1" == slide.getAttribute("prevent")){
                S.Event.preventDefault();
            }
            if("0" == slide.getAttribute("bubble")){
                S.Event.stopPropagation();
            }

            var selector = slide.getAttribute("real");
            var vertical = ("1" == slide.getAttribute("dir"));
            var property = slide.getAttribute("style") ;
            var touch = G_SUPPORT_TOUCH ? e.targetTouches[0] : e;
            var size = slide.getRealSize("size");
            var touchsize = slide.getRealSize("touchsize");
            var scale = (size / touchsize);
            var shift = slide.getAttribute("shift");
            var half = Math.round(size * shift);
            var fingerX = touch.pageX||touch.clientX;
            var fingerY = touch.pageY||touch.clientY;
            var fingerStartX = slide.fingerStartX;
            var fingerStartY = slide.fingerStartY;
            var offset = vertical ? (fingerY - fingerStartY) : (fingerX - fingerStartX);
            var position = slide.currentPoint + (offset * scale);

            if(!vertical){
                slide.left = !(slide.right = (fingerX > fingerStartX));
                slide.up = slide.down = false;
            }else{
                slide.up = !(slide.down = (fingerY > fingerStartY));
                slide.left = slide.right = false;
            }

            //console.info("start : " + fingerStartX + "; current = " + fingerX + "; offset = " + offset + "; half = " + half)

            slide.changed = (Math.abs(offset) >= half);
            slide.currentOffset = position;

            slide.execCallback("move", [e, slide, position]);
            slide.turnScreen(selector, position, property);
        },
        /**
         * touchend事件监听回调函数
         * @param Event e
         */
        bindEndHandler : function(e){
            var slide = G_CURRENT_TOUCH_SLIDE;
            if("1" == slide.getAttribute("prevent")){
                S.Event.preventDefault();
            }
            if("0" == slide.getAttribute("bubble")){
                S.Event.stopPropagation();
            }

            var target = G_SUPPORT_TOUCH ? this : document;
            var selector = slide.getAttribute("real");
            var property = slide.getAttribute("style");
            var size = slide.getRealSize("size");
            var page = slide.getAttribute("page");
            var start = slide.getAttribute("start");
            var isSetStart = (start > -1);
            var isSetEnd = (end > -1);
            var end = slide.getAttribute("end");
            var left = parseInt(slide.currentOffset || (isSetStart ? start : 0), 10);
            var max = size * Math.max(page - 1, 0);
            var endPoint = Math.abs(left);
            var mod = Math.abs(endPoint % size);
            var offset = 0;

            max = (isSetStart ? max - start : max);

            if(slide.changed){
                if(slide.right || slide.down){ //turn right/down
                    offset = 0;
                    var _tmp = (isSetStart ? start : 0);
                    if(left <= _tmp){
                        offset = endPoint - mod;

                        if(left > 0){
                            offset = Math.max(offset, _tmp);
                        }else{
                            offset = Math.min(-offset, _tmp);
                        }
                    }else{
                        offset = _tmp;
                    }
                }else{ //turn left/up
                    offset = endPoint + (size - mod);

                    if(left > 0){
                        offset = Math.min(offset, 0);
                    }else{
                        offset = Math.max(-offset, (isSetEnd ? end : -max));
                    }
                }
            }else{
                offset = slide.currentPoint;
            }

            slide.execCallback("end", [e, slide, offset]);
            slide.turnScreen(selector, offset, property);

            //移除监听事件
            S.Event.remove(target, G_MOVE_EVENT, slide.bindMoveHandler, false);
            S.Event.remove(target, G_END_EVENT, slide.bindEndHandler, false);
        },
        /**
         * 翻屏
         * @param String selector 选择器
         * @param Number _offset 偏移值
         * @param String property CSS属性名
         */
        turnScreen : function(selector, _offset, property){
            var _slide = S.Node.one(selector);
            _slide.style[property] = _offset+'px';
            _slide = null;
        },
        /**
         * 获取真实尺寸
         * @param String attr
         * @return int size
         */
        getRealSize : function(attr){
            var size = this.getAttribute(attr);
            var scr = S.Util.getViewportSize();

            if("device-width" == size){
                size = scr.width;
            }else if("device-height" == size){
                size = scr.height;
            }

            scr = null;
            return parseInt(size, 10);
        },
        /**
         * 设置属性
         * <tag data-touch="name" data-real="selector" data-style="marginLeft" data-dir="0" data-size="200" data-data-touchsize="device-width" data-page="3"  data-shift="0" data-prevent="1" data-bubble="0" data-start="0" data-end="-1"></tag>
         * data-touch := name   TouchSlide实例名称，由程序自动设置
         * data-real  := selector  真正需要控制，没有时为本身
         * data-style := ""   样式属性
         * data-dir   := 0    方向
         * data-size  := [n|device-width|device-height]  一屏尺寸
         * data-touchsize  := [n|device-width|device-height]  滑动尺寸
         * data-page  := 1    屏数
         * data-shift := 0    偏移多少进行移动[0...1]之间
         * data-prevent := 1  是否阻止默认行为
         * data-bubble  := 0  是否阻止冒泡
         * data-start := -1 起始位置，小于0时为0.
         * data-end := -1 结束位置，小于0时不起作用
         *
         * @param String selector css选择符
         */
        setTouchSlideAttrs : function(selector){
            var o = S.Node.one(selector);

            this.setAttribute("touch", this.name);
            this.setAttribute("selector", selector);
            this.setAttribute("real", o.getAttribute("data-real") || selector);
            this.setAttribute("dir", o.getAttribute("data-dir")||"0");
            this.setAttribute("style", o.getAttribute("data-style")||"marginLeft");
            this.setAttribute("size", o.getAttribute("data-size"));
            this.setAttribute("touchsize", o.getAttribute("data-touchsize")||this.getAttribute("size"));
            this.setAttribute("page", parseInt(o.getAttribute("data-page")||1, 10));
            this.setAttribute("prevent", o.getAttribute("data-prevent") || "1");
            this.setAttribute("bubble", o.getAttribute("data-bubble") || "0");
            this.setAttribute("shift", parseFloat(o.getAttribute("data-shift") || 0, 10));
            this.setAttribute("start", parseInt(o.getAttribute("data-start") || -1, 10));
            this.setAttribute("end", parseInt(o.getAttribute("data-end") || -1, 10));

            o = null;
        },
        /**
         * 核心绑定
         * @param String selector 选择器
         * @param String type 调用类型
         */
        bindCore : function(selector, type){
            var o = S.Node.one(selector);
            if(null != o){
                this.execCallback("before", [o]);

                //设置相关属性
                this.setTouchSlideAttrs(selector);

                o.setAttribute("data-touch", this.name);

                var check = this.get("beforecheck");
                var page = this.getAttribute("page");

                if(page > 1 &&   //个数大于1
                    (null == check ||  //没有设置beforecheck
                        (null != check && true !== check.returnValue) || //没有设置returnValue属性或returnValue属性不为true
                            (null != check && this.execCallback("beforecheck", [o])) //有设置beforecheck并且条件为真
                    )
                ){
                    S.Event.on(o, G_START_EVENT, ("bind" == type ? this.bindStartHandler : this.touchStartHandler), false);
                }
            }
            o = null;
        },
        /**
         * 绑定
         * @param String selector 选择器
         * @see bindCore
         */
        bind : function(selector){
            this.bindCore(selector, "bind");
        },
        /**
         * touchstart事件监听回调函数
         * @param Event e
         */
        touchStartHandler : function(e){
            var slide = GetTouchSlide(this.getAttribute("data-touch"));
            G_CURRENT_TOUCH_SLIDE = slide;

            if("1" == slide.getAttribute("prevent")){
                S.Event.preventDefault();
            }
            if("0" == slide.getAttribute("bubble")){
                S.Event.stopPropagation();
            }

            var property = slide.getAttribute("style");
            var touch = G_SUPPORT_TOUCH ? e.targetTouches[0] : e;
            var target = G_SUPPORT_TOUCH ? this : document;

            slide.fingerStartX = touch.pageX || touch.clientX;
            slide.fingerStartY = touch.pageY || touch.clientY;

            //当前层的位置
            slide.currentPoint = parseInt(this.style[property] || 0, 10);
            //touch move
            S.Event.on(target, G_MOVE_EVENT, slide.touchMoveHandler, false);
            S.Event.on(target, G_END_EVENT, slide.touchEndHandler, false);

            slide.execCallback("start", [e, slide]);
        },
        /**
         * touchmove事件监听回调函数
         * @param Event e
         */
        touchMoveHandler : function(e){
            var slide = G_CURRENT_TOUCH_SLIDE;
            if("1" == slide.getAttribute("prevent")){
                S.Event.preventDefault();
            }
            if("0" == slide.getAttribute("bubble")){
                S.Event.stopPropagation();
            }

            var vertical = ("1" == slide.getAttribute("dir"));
            var touch = G_SUPPORT_TOUCH ? e.targetTouches[0] : e;
            var fingerX = touch.pageX || touch.clientX;
            var fingerY = touch.pageY || touch.clientY;
            var fingerStartX = slide.fingerStartX;
            var fingerStartY = slide.fingerStartY;
            var offset = vertical ? (fingerY - fingerStartY) : (fingerX - fingerStartX);

            offset = slide.currentPoint + offset;
            if(!vertical){
                slide.left = !(slide.right = (fingerX > fingerStartX));
                slide.up = slide.down = false;
            }else{
                slide.up = !(slide.down = (fingerY > fingerStartY));
                slide.left = slide.right = false;
            }
            slide.execCallback("move", [e, slide, offset, touch]);
        },
        /**
         * touchend事件监听回调函数
         * @param Event e
         */
        touchEndHandler : function(e){
            var slide = G_CURRENT_TOUCH_SLIDE;
            if("1" == slide.getAttribute("prevent")){
                S.Event.preventDefault();
            }
            if("0" == slide.getAttribute("bubble")){
                S.Event.stopPropagation();
            }

            var target = G_SUPPORT_TOUCH ? this : document;

            slide.execCallback("end", [e, slide]);

            //移除监听事件
            S.Event.remove(target, G_MOVE_EVENT, slide.touchMoveHandler, false);
            S.Event.remove(target, G_END_EVENT, slide.touchEndHandler, false);
        },
        /**
         * 绑定Touch事件，需要自己处理相关回调
         * @param String selector 选择器
         * @see bindCore
         */
        touch : function(selector){
            this.bindCore(selector, "touch");
        },
        /**
         * 销毁对象
         */
        destory : function(){
            G_TOUCH_MAP[this.name] = null;
        }
    }; //end _TouchSlide
    //----------------------------------------------------------------
    
    var _o = {
        /**
         * 获取TouchSlide实例
         * @param String name 唯一标识
         * @return Object
         */
        "newInstance" : function(name){
            var instance = null;

            if(!name || typeof(name) != "string"){
                throw new Error("Illegal parameter! Smart.TouchSlide.newInstance(name)::name = " + name);
            }else{
                instance = GetTouchSlide(name, true);

                //方法注册
                return {
                    /**
                     * 设置回调
                     * @see _TouchSlide.set(type, callback, args, context)
                     */
                    "set" : function(type, callback, args, context){instance.set(type, callback, args, context);},
                    /**
                     * 获取回调
                     * @see _TouchSlide.get(type)
                     */
                    "get" : function(type){return instance.get(type);},
                    /**
                     * 移除回调
                     * @see _TouchSlide.remove(type)
                     */
                    "remove" : function(type){instance.remove(type);},
                    /**
                     * 清除所有回调
                     * @see _TouchSlide.clear()
                     */
                    "clear" : function(){instance.clear();},
                    /**
                     * 绑定
                     * @see _TouchSlide.bind(selector)
                     */
                    "bind" : function(selector){instance.bind(selector);},
                    /**
                     * 绑定Touch事件，需要自己处理相关回调
                     * @see _TouchSlide.touch(selector)
                     */
                    "touch" : function(selector){instance.touch(selector);},
                    /**
                     * 销毁TouchSlide实例
                     * @see _TouchSlide.destory()
                     */
                    "destory" : function(){instance.destory();}
                };
            }
        }
    };
    
    S.source("TouchSlide", _o);
});